{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6e83a41b",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-1/deployment.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239303-lesson-8-deployment)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c20242c4-0010-4065-89f6-0e0b16c7da6e",
   "metadata": {},
   "source": [
    "# Deployment\n",
    "\n",
    "## Review \n",
    "\n",
    "We built up to an agent with memory: \n",
    "\n",
    "* `act` - let the model call specific tools \n",
    "* `observe` - pass the tool output back to the model \n",
    "* `reason` - let the model reason about the tool output to decide what to do next (e.g., call another tool or just respond directly)\n",
    "* `persist state` - use an in memory checkpointer to support long-running conversations with interruptions\n",
    "\n",
    "## Goals\n",
    "\n",
    "Now, we'll cover how to actually deploy our agent locally to Studio and to `LangGraph Cloud`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "08d2814e-2c9c-43b2-8d37-cd23d1e838c9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/home/tejasviaynor/langchain-academy/lc-academy-env/bin/python\n"
     ]
    }
   ],
   "source": [
    "!which python"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f348498b-f277-4514-b163-fe5ed9afe6fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langgraph_sdk langchain_core"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "e4d0f4a7-82ee-4458-bd9a-e246ce2dc4ae",
   "metadata": {},
   "source": [
    "## Concepts\n",
    "\n",
    "There are a few central concepts to understand -\n",
    "\n",
    "`LangGraph` —\n",
    "- Python and JavaScript library \n",
    "- Allows creation of agent workflows \n",
    "\n",
    "`LangGraph API` —\n",
    "- Bundles the graph code \n",
    "- Provides a task queue for managing asynchronous operations\n",
    "- Offers persistence for maintaining state across interactions\n",
    "\n",
    "`LangGraph Cloud` --\n",
    "- Hosted service for the LangGraph API\n",
    "- Allows deployment of graphs from GitHub repositories\n",
    "- Also provides monitoring and tracing for deployed graphs\n",
    "- Accessible via a unique URL for each deployment\n",
    "\n",
    "`LangGraph Studio` --\n",
    "- Integrated Development Environment (IDE) for LangGraph applications\n",
    "- Uses the API as its back-end, allowing real-time testing and exploration of graphs\n",
    "- Can be run locally or with cloud-deployment\n",
    "\n",
    "`LangGraph SDK` --\n",
    "- Python library for programmatically interacting with LangGraph graphs\n",
    "- Provides a consistent interface for working with graphs, whether served locally or in the cloud\n",
    "- Allows creation of clients, access to assistants, thread management, and execution of runs\n",
    "\n",
    "## Testing Locally\n",
    "\n",
    "--\n",
    "\n",
    "**⚠️ DISCLAIMER**\n",
    "\n",
    "*Running Studio currently requires a Mac. If you are not using a Mac, then skip this step.*\n",
    "\n",
    "*Also, if you are running this notebook in CoLab, then skip this step.*\n",
    "\n",
    "--\n",
    "\n",
    "We can easily connect with graphs that are served locally in LangGraph Studio!\n",
    "\n",
    "We do this via the `url` provided in the lower left corner of the Studio UI.\n",
    "\n",
    "![Screenshot 2024-08-23 at 1.17.05 PM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbad4f53080e6802cec34d_deployment%201.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "fa75ebd4-91fe-42c5-8122-c81e72133477",
   "metadata": {},
   "outputs": [
    {
     "ename": "Exception",
     "evalue": "Unfortunately LangGraph Studio is currently not supported on Google Colab or requires a Mac",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mException\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[3], line 4\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mplatform\u001b[39;00m\n\u001b[1;32m      3\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mgoogle.colab\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mstr\u001b[39m(get_ipython()) \u001b[38;5;129;01mor\u001b[39;00m platform\u001b[38;5;241m.\u001b[39msystem() \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mDarwin\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[0;32m----> 4\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUnfortunately LangGraph Studio is currently not supported on Google Colab or requires a Mac\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[0;31mException\u001b[0m: Unfortunately LangGraph Studio is currently not supported on Google Colab or requires a Mac"
     ]
    }
   ],
   "source": [
    "import platform\n",
    "\n",
    "if 'google.colab' in str(get_ipython()) or platform.system() != 'Darwin':\n",
    "    raise Exception(\"Unfortunately LangGraph Studio is currently not supported on Google Colab or requires a Mac\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "18b281d8-bd07-4721-922c-347838ceee6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph_sdk import get_client"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4c96f353-5dc3-41c8-a3e4-6bf07ca455f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Replace this with the URL of your own deployed graph\n",
    "URL = \"http://localhost:56091\"\n",
    "client = get_client(url=URL)\n",
    "\n",
    "# Search all hosted graphs\n",
    "assistants = await client.assistants.search()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "6a1352fa-68ad-4963-890e-c95d93570917",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca',\n",
       " 'graph_id': 'agent',\n",
       " 'created_at': '2024-08-30T23:58:18.505075+00:00',\n",
       " 'updated_at': '2024-08-30T23:58:18.505075+00:00',\n",
       " 'config': {},\n",
       " 'metadata': {'created_by': 'system'}}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assistants[-3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ba9c28a0-d712-496c-b191-7d620589ba33",
   "metadata": {},
   "outputs": [],
   "source": [
    "# We create a thread for tracking the state of our run\n",
    "thread = await client.threads.create()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e7e4177-3644-43fa-a2f1-08f73292d1a6",
   "metadata": {},
   "source": [
    "Now, we can run our agent [with `client.runs.stream`](https://langchain-ai.github.io/langgraph/concepts/low_level/#stream-and-astream) with:\n",
    "\n",
    "* The `thread_id`\n",
    "* The `graph_id`\n",
    "* The `input` \n",
    "* The `stream_mode`\n",
    "\n",
    "We'll discuss streaming in depth in a future module. \n",
    "\n",
    "For now, just recognize that we are [streaming](https://langchain-ai.github.io/langgraph/cloud/how-tos/stream_values/) the full value of the state after each step of the graph with `stream_mode=\"values\"`.\n",
    " \n",
    "The state is captured in the `chunk.data`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f65a4480-66b3-48bf-9158-191a7b8c1c18",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'content': 'Multiply 3 by 2.', 'additional_kwargs': {'example': False, 'additional_kwargs': {}, 'response_metadata': {}}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': 'cdbd7bd8-c476-4ad4-8ab7-4ad9e3654267', 'example': False}\n",
      "{'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_iIPryzZZxRtXozwwhVtFObNO', 'function': {'arguments': '{\"a\":3,\"b\":2}', 'name': 'multiply'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-06c7243c-426d-4c81-a113-f1335dda5fb2', 'example': False, 'tool_calls': [{'name': 'multiply', 'args': {'a': 3, 'b': 2}, 'id': 'call_iIPryzZZxRtXozwwhVtFObNO', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}\n",
      "{'content': '6', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'multiply', 'id': '988cb170-f6e6-43c1-82fd-309f519abe6d', 'tool_call_id': 'call_iIPryzZZxRtXozwwhVtFObNO', 'artifact': None, 'status': 'success'}\n",
      "{'content': 'The result of multiplying 3 by 2 is 6.', 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_157b3831f5'}, 'type': 'ai', 'name': None, 'id': 'run-7bda0aa0-6895-4250-9625-18419c5dc171', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "# Input\n",
    "input = {\"messages\": [HumanMessage(content=\"Multiply 3 by 2.\")]}\n",
    "\n",
    "# Stream\n",
    "async for chunk in client.runs.stream(\n",
    "        thread['thread_id'],\n",
    "        \"agent\",\n",
    "        input=input,\n",
    "        stream_mode=\"values\",\n",
    "    ):\n",
    "    if chunk.data and chunk.event != \"metadata\":\n",
    "        print(chunk.data['messages'][-1])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "dfa8b850-750c-4054-95e4-1c457a12ec8a",
   "metadata": {},
   "source": [
    "## Testing with Cloud\n",
    "\n",
    "We can deploy to Cloud via LangSmith, as outlined [here](https://langchain-ai.github.io/langgraph/cloud/quick_start/#deploy-from-github-with-langgraph-cloud). \n",
    "\n",
    "### Create a New Repository on GitHub\n",
    "\n",
    "* Go to your GitHub account\n",
    "* Click on the \"+\" icon in the upper-right corner and select `\"New repository\"`\n",
    "* Name your repository (e.g., `langchain-academy`)\n",
    "\n",
    "### Add Your GitHub Repository as a Remote\n",
    "\n",
    "* Go back to your terminal where you cloned `langchain-academy` at the start of this course\n",
    "* Add your newly created GitHub repository as a remote\n",
    "\n",
    "```\n",
    "git remote add origin https://github.com/your-username/your-repo-name.git\n",
    "```\n",
    "* Push to it\n",
    "```\n",
    "git push -u origin main\n",
    "```\n",
    "\n",
    "### Connect LangSmith to your GitHub Repository\n",
    "\n",
    "* Go to [LangSmith](hhttps://smith.langchain.com/)\n",
    "* Click on `deployments` tab on the left LangSmith panel\n",
    "* Add `+ New Deployment`\n",
    "* Then, select the Github repository (e.g., `langchain-academy`) that you just created for the course\n",
    "* Point the `LangGraph API config file` at one of the `studio` directories\n",
    "* For example, for module-1 use: `module-1/studio/langgraph.json`\n",
    "* Set your API keys (e.g., you can just copy from your `module-1/studio/.env` file)\n",
    "\n",
    "![Screenshot 2024-09-03 at 11.35.12 AM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbad4fd61c93d48e5d0f47_deployment2.png)\n",
    "\n",
    "### Work with your deployment\n",
    "\n",
    "We can then interact with our deployment a few different ways:\n",
    "\n",
    "* With the [SDK](https://langchain-ai.github.io/langgraph/cloud/quick_start/#use-with-the-sdk), as before.\n",
    "* With [LangGraph Studio](https://langchain-ai.github.io/langgraph/cloud/quick_start/#interact-with-your-deployment-via-langgraph-studio).\n",
    "\n",
    "![Screenshot 2024-08-23 at 10.59.36 AM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbad4fa159a09a51d601de_deployment3.png)\n",
    "\n",
    "To use the SDK here in the notebook, simply ensure that `LANGSMITH_API_KEY` is set!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "646ed351",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"LANGCHAIN_API_KEY\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97dda16c-c87f-4c03-b910-d647e83400b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Replace this with the URL of your deployed graph\n",
    "URL = \"https://langchain-academy-8011c561878d50b1883f7ed11b32d720.default.us.langgraph.app\"\n",
    "client = get_client(url=URL)\n",
    "\n",
    "# Search all hosted graphs\n",
    "assistants = await client.assistants.search()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "aefa37c0-92fe-4e80-9d5a-80a77b1e3dae",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Select the agent\n",
    "agent = assistants[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "b810376e-f20f-443a-b1ca-d6793f358f82",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'assistant_id': 'fe096781-5601-53d2-b2f6-0d3403f7e9ca',\n",
       " 'graph_id': 'agent',\n",
       " 'created_at': '2024-08-23T17:58:02.722920+00:00',\n",
       " 'updated_at': '2024-08-23T17:58:02.722920+00:00',\n",
       " 'config': {},\n",
       " 'metadata': {'created_by': 'system'}}"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "32d65d84-1bcf-4af4-a7c9-55e73d6c1947",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'content': 'Multiply 3 by 2.', 'additional_kwargs': {'example': False, 'additional_kwargs': {}, 'response_metadata': {}}, 'response_metadata': {}, 'type': 'human', 'name': None, 'id': '8ea04559-f7d4-4c82-89d9-c60fb0502f21', 'example': False}\n",
      "{'content': '', 'additional_kwargs': {'tool_calls': [{'index': 0, 'id': 'call_EQoolxFaaSVU8HrTnCmffLk7', 'function': {'arguments': '{\"a\":3,\"b\":2}', 'name': 'multiply'}, 'type': 'function'}]}, 'response_metadata': {'finish_reason': 'tool_calls', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, 'type': 'ai', 'name': None, 'id': 'run-b0ea5ddd-e9ba-4242-bb8c-80eb52466c76', 'example': False, 'tool_calls': [{'name': 'multiply', 'args': {'a': 3, 'b': 2}, 'id': 'call_EQoolxFaaSVU8HrTnCmffLk7', 'type': 'tool_call'}], 'invalid_tool_calls': [], 'usage_metadata': None}\n",
      "{'content': '6', 'additional_kwargs': {}, 'response_metadata': {}, 'type': 'tool', 'name': 'multiply', 'id': '1bf558e7-79ef-4f21-bb66-acafbd04677a', 'tool_call_id': 'call_EQoolxFaaSVU8HrTnCmffLk7', 'artifact': None, 'status': 'success'}\n",
      "{'content': '3 multiplied by 2 equals 6.', 'additional_kwargs': {}, 'response_metadata': {'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, 'type': 'ai', 'name': None, 'id': 'run-ecc4b6ad-af15-4a85-a76c-de2ed0ed8ed9', 'example': False, 'tool_calls': [], 'invalid_tool_calls': [], 'usage_metadata': None}\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "# We create a thread for tracking the state of our run\n",
    "thread = await client.threads.create()\n",
    "\n",
    "# Input\n",
    "input = {\"messages\": [HumanMessage(content=\"Multiply 3 by 2.\")]}\n",
    "\n",
    "# Stream\n",
    "async for chunk in client.runs.stream(\n",
    "        thread['thread_id'],\n",
    "        \"agent\",\n",
    "        input=input,\n",
    "        stream_mode=\"values\",\n",
    "    ):\n",
    "    if chunk.data and chunk.event != \"metadata\":\n",
    "        print(chunk.data['messages'][-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "445cb34d-c3b8-4446-a7e3-5fe938abf99b",
   "metadata": {},
   "outputs": [],
   "source": [
    "#SKIPPED NOTEBOOK\n",
    "#As outlined in the online course, must be on Plus plan to use cloud deployment feature\n",
    "#Don't think i need this for the onboarding assignment, will come back to this if i do later on"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
